import json
import html
import re
from collections import defaultdict
from typing import List, Dict
from index import Index, DocInfo, InvertedElem, JiebaUtil
from jieba import cut


class InvertedElemPrint:
    """用于存储搜索过程中每个文档的匹配结果，包括文档ID、匹配权重以及命中的关键词列表。"""
    def __init__(self):
        self.doc_id = 0  # 文档 ID
        self.weight = 0  # 匹配权重
        self.words = []  # 匹配到的关键词列表


class Searcher:
    """搜索器类，负责初始化索引和处理搜索请求。"""
    def __init__(self):
        # 获取索引单例，用于管理正排索引和倒排索引
        self.index = Index.get_instance()

    def init_searcher(self, input_path: str):
        """
        初始化搜索器并建立索引。
        :param input_path: 输入文件路径，用于加载文档数据
        """
        if self.index.build_index(input_path):  # 调用索引模块构建索引
            print("索引建立成功！")
        else:
            print("索引建立失败！")

    def search(self, query: str):
        """
        处理搜索请求，根据查询字符串返回搜索结果。
        :param query: 用户输入的搜索关键字
        :return: 搜索结果的 Python 数据结构（包含文档标题、摘要、URL 等信息）
        """
        # Step 1: 分词处理
        words = self._cut_string(query)

        # Step 2: 获取倒排索引列表并累积权重
        tokens_map: Dict[int, InvertedElemPrint] = defaultdict(InvertedElemPrint)
        for word in words:
            # 根据词获取倒排索引列表
            inverted_list = self.index.get_inverted_list(word.lower())
            if not inverted_list:
                continue  # 如果倒排索引为空，则跳过该词

            # 遍历倒排索引列表，累积权重并记录命中的词
            for elem in inverted_list:
                item = tokens_map[elem.doc_id]
                item.doc_id = elem.doc_id
                item.weight += elem.weight      # 累加权重
                item.words.append(elem.word)    # 记录命中的关键词

        # Step 3: 按权重排序文档
        inverted_list_all = sorted(
            tokens_map.values(), key=lambda x: x.weight, reverse=True
        )

        # Step 4: 构建搜索结果列表
        result = []
        for item in inverted_list_all:
            # 根据文档 ID 获取正排索引信息（标题、内容、URL）
            doc = self.index.get_forward_index(item.doc_id)
            if not doc:
                continue  # 如果文档不存在，跳过该结果

            # 构造单条搜索结果
            result_item = {
                "title": doc.title,                                     # 文档标题
                "desc": self._get_desc(doc.content, item.words[0]),     # 摘要信息
                "url": doc.url,                                         # 文档 URL
                "id": item.doc_id,                                      # 文档 ID
                "weight": item.weight                                   # 匹配权重
            }

            # 将结果添加到结果列表中
            result.append(result_item)

        # 返回搜索结果列表
        return result

    def _cut_string(self, text: str) -> List[str]:
        """
        分词工具，使用 Jieba 对查询字符串进行分词。
        :param text: 输入的查询字符串
        :return: 分词结果列表
        """
        # 使用 Jieba 工具类进行分词
        jieba_util = JiebaUtil.get_instance()
        return jieba_util.cut_string(text)

    def _get_desc(self, html_content: str, word: str) -> str:
        """
        生成摘要信息，返回包含关键词上下文的文本片段。
        :param html_content: 文档内容
        :param word: 匹配到的关键词
        :return: 文档摘要信息
        """
        prev_step = 50      # 摘要中关键词前的字符数
        next_step = 100     # 摘要中关键词后的字符数

        # 使用正则表达式搜索关键词首次出现的位置，忽略大小写
        match = re.search(re.escape(word), html_content, re.IGNORECASE)
        if not match:
            return "None"  # 如果未匹配到关键词，返回 "None"

        # 确定摘要截取的起始和结束位置
        pos = match.start()
        start = max(0, pos - prev_step)                 # 避免越界，起始位置不能小于 0
        end = min(len(html_content), pos + next_step)   # 避免越界，结束位置不能超出内容长度

        # 截取摘要片段
        desc = html_content[start:end]
        return desc + "..." if end < len(html_content) else desc
